﻿using AutoMapper;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
using Wuther.Api.ActionConstraints;
using Wuther.Bussiness.Interface;
using Wuther.Entities.Models;
using Wuther.Util.DtoParameters;
using Wuther.Util.Enums;
using Wuther.Util.Helper;
using Wuther.Util.Models;
using Wuther.Util.PropertyMapping;

namespace Wuther.Api.Controllers
{
    [Route("api/[controller]"), ApiExplorerSettings(GroupName = "Article")]
    public class ArticleController : BaseController
    {
        private readonly IMapper _mapper;
        private readonly IArticleRepository _ArticleRepository;
        //private readonly IUserRepository _userRepository;
        private readonly IMenuRepository _menuRepository;
        private readonly IPropertyMappingService _propertyMappingService;
        private readonly IPropertyCheckerService _propertyCheckerService;
        private readonly IConfiguration _configuration;
        private readonly ILogger<ArticleController> _log;

        public ArticleController(IMapper mapper,
            IArticleRepository ArticleRepository,
            //IUserRepository userRepository,
            IMenuRepository menuRepository,
            IPropertyMappingService propertyMappingService,
            IPropertyCheckerService propertyCheckerService,
            IConfiguration configuration,
            ILogger<ArticleController> log)
        {
            _mapper = mapper;
            _ArticleRepository = ArticleRepository;
            //_userRepository = userRepository;
            _menuRepository = menuRepository;
            _propertyMappingService = propertyMappingService;
            _propertyCheckerService = propertyCheckerService;
            _configuration = configuration;
            _log = log;
        }
        [Produces("application/json",
            "application/vnd.wuther.hateoas+json",
            "application/vnd.wuther.Article.friendly+json",
            "application/vnd.wuther.Article.friendly.hateoas+json",
            "application/vnd.wuther.Article.full+json",
            "application/vnd.wuther.Article.full.hateoas+json")]
        [HttpGet("{articleId}", Name = nameof(GetArticle))]
        public async Task<IActionResult> GetArticle(int articleId, string fields, [FromHeader(Name = "Accept")] string mediaType)
        {
            if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue parsedMediaType))
            {
                return BadRequest();
            }
            if (!_propertyCheckerService.TypeHasProperties<ArticleDto>(fields))
            {
                return BadRequest();
            }


            var includeLinks = parsedMediaType.SubTypeWithoutSuffix.
                EndsWith("hateoas", System.StringComparison.InvariantCultureIgnoreCase);
            IEnumerable<LinkDto> myLinks = new List<LinkDto>();
            if (includeLinks)
            {
                myLinks = CreateLinksForArticle(articleId, fields);
            }

            var primaryMediaType = includeLinks
                ? parsedMediaType.SubTypeWithoutSuffix.Substring(0, parsedMediaType.SubTypeWithoutSuffix.Length - 8)
                : parsedMediaType.SubTypeWithoutSuffix;
            if (primaryMediaType == "vnd.wuther.Article.full")
            {
                var article = await _ArticleRepository.GetArticleAsync(articleId);
                if (article == null)
                {
                    return NotFound();
                }
                var full = _mapper.Map<Article, ArticleFullDto>(article);
                var fullDic = full.ShapeData(fields) as IDictionary<string, object>;
                if (includeLinks)
                {
                    fullDic.Add("links", myLinks);
                }
                return Ok(full);
            }
            else
            {
                var article = await _ArticleRepository.FindAsync(articleId);
                if (article == null)
                {
                    return NotFound();
                }
                var friendly = _mapper.Map<ArticleDto>(article).ShapeData(fields) as IDictionary<string, object>;

                if (includeLinks)
                {
                    friendly.Add("links", myLinks);
                }

                return Ok(friendly);
            }
        }

        [Produces("application/json",
            "application/vnd.wuther.hateoas+json",
            "application/vnd.wuther.Article.friendly+json",
            "application/vnd.wuther.Article.friendly.hateoas+json",
            "application/vnd.wuther.Article.full+json",
            "application/vnd.wuther.Article.full.hateoas+json")]
        [HttpGet(Name = nameof(GetArticles))]
        public async Task<IActionResult> GetArticles([FromQuery] DtoArticleParameter parameter, [FromHeader(Name = "Accept")] string mediaType)
        {
            if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue parsedMediaType))
            {
                return BadRequest();
            }
            if (!_propertyCheckerService.TypeHasProperties<ArticleDto>(parameter.Fields))
            {
                return BadRequest();
            }
            if (!_propertyMappingService.ValidMappingExistsFor<ArticleDto, Article>(parameter.OrderBy))
            {
                return BadRequest();
            }
            var includeLinks = parsedMediaType.SubTypeWithoutSuffix.
                EndsWith("hateoas", System.StringComparison.InvariantCultureIgnoreCase);
            IEnumerable<LinkDto> myLinks = new List<LinkDto>();
            var primaryMediaType = includeLinks
                ? parsedMediaType.SubTypeWithoutSuffix.Substring(0, parsedMediaType.SubTypeWithoutSuffix.Length - 8)
                : parsedMediaType.SubTypeWithoutSuffix;


            if (primaryMediaType == "vnd.wuther.Article.full")
            {
                var fullArticle = await _ArticleRepository.GetArticlesAsync(parameter);
                var fullPaginationMetadata = new
                {
                    totalCount = fullArticle.TotalCount,
                    pageSize = fullArticle.PageSize,
                    currentPage = fullArticle.CurrentPage,
                    totalPages = fullArticle.TotalPages,
                };
                Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(fullPaginationMetadata, new JsonSerializerOptions
                {
                    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
                }));
                var articleFullDtos = _mapper.Map<IList<ArticleFullDto>>(fullArticle);
                var shapedFullData = articleFullDtos.shapeData(parameter.Fields);
                var fullLinks = CreateLinksForMenus(parameter, fullArticle.HasPrevious, fullArticle.HasNext);
                var fullLinkedCollectionResource = new
                {
                    value = shapedFullData,
                    fullLinks
                };

                return Ok(fullLinkedCollectionResource);

            }
            var Article = await _ArticleRepository.GetArticlesAsync(parameter);
            var paginationMetadata = new
            {
                totalCount = Article.TotalCount,
                pageSize = Article.PageSize,
                currentPage = Article.CurrentPage,
                totalPages = Article.TotalPages,
            };
            Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(paginationMetadata, new JsonSerializerOptions
            {
                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
            }));

            var articleDtos = _mapper.Map<IList<ArticleDto>>(Article);
            var shapedData = articleDtos.shapeData(parameter.Fields);
            var links = CreateLinksForMenus(parameter, Article.HasPrevious, Article.HasNext);

            //var shapedMenusWithLinks = shapedData.Select(c =>
            //{
            //    var menuDict = c as IDictionary<string, object>;
            //    var menusLinks = CreateLinksForMenus((int)menuDict["Id"], null);
            //    menuDict.Add("links", menusLinks);
            //    return menuDict;
            //});

            var linkedCollectionResource = new
            {
                value = shapedData,
                links
            };

            return Ok(linkedCollectionResource);
        }
        [Authorize]
        [HttpPost(Name = nameof(CreateArticle))]
        [RequestHeaderMatchesMediaType("Content-Type", "application/json", "application/vnd.wuther.articleforcreation+json")]
        [Consumes("application/json", "application/vnd.wuther.articleforcreation+json")]
        public async Task<ActionResult<Article>> CreateArticle(ArticleAddDto article)
        {
            //创建html文件
            //1.设置文件名
            var fileName = DateTimeHelper.GetTimestamp(DateTime.Now);
            //创建文件
            var htmlHead = System.IO.File.ReadAllText("./Resource/articlehead.txt");
            var htmlfoot = System.IO.File.ReadAllText("./Resource/articlefoot.txt");
            var htmlContent = $"{htmlHead}{article.Content}{htmlfoot}";

            var buffer = Encoding.UTF8.GetBytes(htmlContent);
            var path = _configuration.GetSection("UiPath").GetSection("ArticlePath").Value;
            //var user = User.Identity.Name;
            var token = HttpContext.Request.Headers["Authorization"].ToString();
            var apiClient = new HttpClient();
            apiClient.SetBearerToken(token.Remove(0, 6));
            var response = await apiClient.GetAsync("http://47.119.127.197:6002/connect/userinfo");
            if (!response.IsSuccessStatusCode)
            {
                return Unauthorized();
            }
            var content = await response.Content.ReadAsStringAsync();
            var userInfo = JsonSerializer.Deserialize<IdentityUserInfo>(content);
            var filePath = Path.Combine(path, userInfo.preferred_username.ToString(), "a");
            if (!FileHelper.IsExistDirectory(filePath))
            {
                FileHelper.CreateDirectory(filePath);
            }
            var filename = Path.Combine(filePath, $"{fileName}.html");

            _log.LogInformation(filename);
            FileHelper.CreateFile(filename, buffer);

            var entity = _mapper.Map<Article>(article);
            entity.CreateTime = DateTime.Now;
            entity.Path = $"{userInfo.preferred_username}\\a\\{fileName}.html";
            var contentStr = StringHelper.RemoveBlogContentSpecialChar(article.Content);
            if (contentStr.Length > 200)
            {
                entity.Abstract = contentStr.Substring(0, 200);
            }
            else
            {
                entity.Abstract = contentStr.Substring(0, contentStr.Length);
            }
            var articleAdd = await _ArticleRepository.InsertAsync(entity);
            var returnDto = _mapper.Map<ArticleDto>(articleAdd);
            var links = CreateLinksForArticle(returnDto.Id, null);
            var linkedDict = returnDto.ShapeData(null) as IDictionary<string, object>;
            linkedDict.Add("links", links);
            //var xx = CreatedAtRoute(nameof(GetArticle), new { menuId = linkedDict["Id"], fields = "", mediaType = "" }, linkedDict);
            //return xx;
            return Ok(linkedDict);
        }

        private IEnumerable<LinkDto> CreateLinksForArticle(int menuId, string fields)
        {
            var links = new List<LinkDto>();

            if (string.IsNullOrWhiteSpace(fields))
            {
                links.Add(new LinkDto(Url.Link(nameof(GetArticle), new { menuId }),
                    "self",
                    "GET"));
            }
            else
            {
                links.Add(new LinkDto(Url.Link(nameof(GetArticle), new { menuId, fields }),
                    "self",
                    "GET"));
            }
            //links.Add(new LinkDto(Url.Link(nameof(DeleteMenu), new { userId }), "delete menu", "DELETE"));

            //links.Add(new LinkDto(Url.Link(nameof(CreateMenu), new { }), "create menu", "POST"));

            links.Add(new LinkDto(Url.Link(nameof(GetArticle), new { }), "get menus", "GET"));
            return links;
        }

        private IEnumerable<LinkDto> CreateLinksForMenus(DtoArticleParameter parameters, bool hasPrevious, bool hasNext)
        {
            var links = new List<LinkDto>();


            links.Add(new LinkDto(CreateArticleResourceUri(parameters, ResourceUriType.CurrentPage),
                "self", "GET"));

            if (hasPrevious)
            {
                links.Add(new LinkDto(CreateArticleResourceUri(parameters, ResourceUriType.PreviousPage),
                    "previous_page", "GET"));
            }

            if (hasNext)
            {
                links.Add(new LinkDto(CreateArticleResourceUri(parameters, ResourceUriType.NextPage),
                    "next_page", "GET"));
            }

            return links;
        }

        private string CreateArticleResourceUri(DtoArticleParameter parameters,
            ResourceUriType type)
        {
            switch (type)
            {
                case ResourceUriType.PreviousPage:
                    return Url.Link(nameof(GetArticles), new
                    {
                        fields = parameters.Fields,
                        pageNumber = parameters.PageNumber - 1,
                        pageSize = parameters.PageSize,
                        title = parameters.Title,
                        summary = parameters.Abstract,
                        orderBy = parameters.OrderBy
                    });

                case ResourceUriType.NextPage:

                    return Url.Link(nameof(GetArticles), new
                    {
                        fields = parameters.Fields,
                        pageNumber = parameters.PageNumber + 1,
                        pageSize = parameters.PageSize,
                        title = parameters.Title,
                        summary = parameters.Abstract,
                        orderBy = parameters.OrderBy
                    });

                case ResourceUriType.CurrentPage:
                default:
                    return Url.Link(nameof(GetArticles), new
                    {
                        fields = parameters.Fields,
                        pageNumber = parameters.PageNumber,
                        pageSize = parameters.PageSize,
                        title = parameters.Title,
                        summary = parameters.Abstract,
                        orderBy = parameters.OrderBy
                    });
            }
        }
    }
}
